/*
 * Copyright (c) 2024, Qi Yaolong.
 * dragon@hbu.edu.cn
 * HeBei University, China.
 * 
 * 简体中文
 * 保留所有权利。依据GPL 2.0（以下简称"本许可"）进行许可。除非符合（遵守）本许可及以下条款，否则
 * 您不得使用此文件：
 * 
 * 禁止任何未经授权的个人或组织以任何形式将本程序及其附属资料、创新、创意、架构设计、算法、衍生作品
 * 等用于任何商业目的、盈利活动、各类竞赛(比赛)、直播教学、录播教学、线下课程、书籍编写、教材编写、
 * 会议、培训、公益活动、项目课题、毕业设计、毕业论文、学术论文等。
 * 
 * 本程序由版权持有人和贡献人“按原样”提供，不提供任何明示或暗示担保，包括但不局限于对适销性和特定
 * 用途适合性的暗示担保。在任何情况下，版权持有人或贡献人对因使用本程序而导致的任何直接、间接、附带、
 * 特殊、典型或后果性损害（包括但不限于购买替代商品或服务；使用损失、数据丢失或利润损失；业务中断）
 * 不承担任何责任，无论其如何产生，也不论其责任理论为何，无论是合同、严格责任还是侵权（包括疏忽或
 * 其他），即使已告知此类损害的可能性。
 * 
 * English
 * All rights reserved. Licensed under the GPL 2.0 (the "License"). You may NOT 
 * use this file except in compliance with the License and the following terms:
 * 
 * IT IS PROHIBITED FOR ANY UNAUTHORIZED PERSON OR ENTITY TO USE THIS PROGRAM AND 
 * ITS ANCILLARY DATA, INNOVATIONS, CONCEPTS, ARCHITECTURE DESIGN, ALGORITHMS, 
 * DERIVED WORKS, ETC., IN ANY FORM FOR COMMERCIAL PURPOSES, PROFIT-MAKING 
 * ACTIVITIES, VARIOUS COMPETITIONS (CONTESTS), LIVE TEACHING, PRE-RECORDED 
 * LESSONS, OFFLINE COURSES, BOOK WRITING, CONFERENCES, TRAININGS, PUBLIC WELFARE 
 * ACTIVITIES, PROJECTS, TOPICS, GRADUATION DESIGNS, DISSERTATIONS, THESES, AND 
 * OTHER APPLICATIONS.
 * 
 * THIS PROGRAM IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS PROGRAM, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "stdlib.h"
#include "ohos_init.h" // 用于初始化服务(services)和功能(features)
#include "cmsis_os2.h" // CMSIS-RTOS API V2

// lwIP：A Lightweight TCPIP stack
// 瑞典计算机科学院(SICS)的Adam Dunkels开发的一个小型(轻量)开源的TCP/IP协议栈
// 实现的重点是在保持TCP协议主要功能的基础上减少对RAM的占用
// 仅占用几十KB RAM空间，40KB ROM空间，非常适合在嵌入式系统中使用
//
// lwIP TCP/IP协议栈：网络接口API
// netifapi：Network Interfaces API
#include "lwip/netifapi.h"
//
// lwIP TCP/IP协议栈：SHELL命令API
// #include "lwip/api_shell.h"
#include "lwip/nettool/misc.h"

#include "soc_osal.h"
#include "errcode.h"
#include "wifi_device.h"
#include "wifi_hotspot.h"
#include "wifi_hotspot_config.h"
#include "wifi_linked_info.h"
#include "wifi_device_config.h"
#include "wifi_connecter.h"


/*********************************************************************************************
 * 
 * 
 * 
 * 
 * 
 * 
 *********************************************************************************************/

static _IPAddrPkgs_ mWifiAddr = {
    .ip_addr = 0,
    .flag_ipAddrst = 0,
};

/**
 * @brief Get the WifiIPaddress object
 * 
 * @return _IPAddrPkgs_* 
 */
_IPAddrPkgs_* get_WifiIPaddress(void)
{
    return (&mWifiAddr);
}

// STA 扫描-关联
/**
 * @brief Get the match network object
 * 
 * @param expected_ssid 
 * @param key 
 * @param expected_bss 
 * @return errcode_t 
 */
errcode_t get_match_network(char *expected_ssid,
                            char *key,
                            wifi_sta_config_stru *expected_bss)
{
    uint32_t num = WIFI_SCAN_AP_LIMIT; /* 64:扫描到的Wi-Fi网络数量 */
    uint32_t bss_index = 0;

    /* 获取扫描结果 */
    uint32_t scan_len = sizeof(wifi_scan_info_stru) * WIFI_SCAN_AP_LIMIT;
    wifi_scan_info_stru *result = osal_kmalloc(scan_len, OSAL_GFP_ATOMIC);
    if (result == NULL)
    {
        return ERRCODE_MALLOC;
    }
    memset_s(result, scan_len, 0, scan_len);
    if (wifi_sta_get_scan_info(result, &num) != ERRCODE_SUCC)
    {
        osal_kfree(result);
        return ERRCODE_FAIL;
    }

    /* 筛选扫描到的Wi-Fi网络，选择待连接的网络 */
    for (bss_index = 0; bss_index < num; bss_index++)
    {
        if (strlen(expected_ssid) == strlen(result[bss_index].ssid))
        // if (strcmp(expected_ssid, result[bss_index].ssid) == 0)
        {
            if (memcmp(expected_ssid, result[bss_index].ssid, strlen(expected_ssid)) == 0)
            {
                break;
            }
        }
    }

    /* 未找到待连接AP,可以继续尝试扫描或者退出 */
    if (bss_index >= num)
    {
        osal_kfree(result);
        return ERRCODE_FAIL;
    }

    /* 找到网络后复制网络信息和接入密码 */
    if (memcpy_s(expected_bss->ssid, WIFI_MAX_SSID_LEN, result[bss_index].ssid, WIFI_MAX_SSID_LEN) != EOK)
    {
        osal_kfree(result);
        return ERRCODE_MEMCPY;
    }
    // 非必须
    // if (memcpy_s(expected_bss->bssid, WIFI_MAC_LEN, result[bss_index].bssid, WIFI_MAC_LEN) != EOK)
    // {
    //     osal_kfree(result);
    //     return ERRCODE_MEMCPY;
    // }
    expected_bss->security_type = result[bss_index].security_type;
    if (memcpy_s(expected_bss->pre_shared_key, WIFI_MAX_KEY_LEN, key, strlen(key)) != EOK)
    {
        osal_kfree(result);
        return ERRCODE_MEMCPY;
    }
    expected_bss->ip_type = DHCP; /* IP类型为动态DHCP获取 */

    osal_kfree(result);
    return ERRCODE_SUCC;
}

/**
 * @brief 
 * 
 * @param ssid 
 * @param key 
 * @return errcode_t 
 */
errcode_t ConnectToHotspot(char *ssid, char *key)
{
    /* WiFi STA 网络设备名，SDK默认是wlan0, 以实际名称为准 */
    char ifname[WIFI_IFNAME_MAX_SIZE + 1] = "wlan0";

    /* 连接请求信息 */
    wifi_sta_config_stru expected_bss = {0};

    // 有关连接到此STA的AP信息
    wifi_linked_info_stru wifi_status;

    /* 创建STA */
    osal_printk("STA try enable.\r\n");
    while (wifi_sta_enable() != ERRCODE_SUCC)
    {
        osal_printk("STA enable fail, retry...\r\n");
        osal_msleep(100);
    }

    // 扫描并连接到AP
    while (1)
    {
        // 启动STA扫描。每次触发扫描至少间隔1s
        osal_msleep(1000);
        osal_printk("Start Scan !\r\n");
        if (wifi_sta_scan() != ERRCODE_SUCC)
        {
            osal_printk("STA scan fail, try again !\r\n");
            continue;
        }

        /* 延时3s, 等待扫描完成 */
        osal_msleep(3000);

        /* 获取待连接的网络 */
        if (get_match_network(ssid, key, &expected_bss) != ERRCODE_SUCC)
        {
            osal_printk("Can not find AP, try again !\r\n");
            continue;
        }

        osal_printk("STA try connect.\r\n");
        /* 启动连接 */
        if (wifi_sta_connect(&expected_bss) != ERRCODE_SUCC)
        {
            continue;
        }

        /* 检查网络是否连接成功 */
        uint8_t index = 0;
        for (index = 0; index < WIFI_CONN_STATUS_MAX_GET_TIMES; index++)
        {
            osal_msleep(500); /* 延时500ms */
            memset_s(&wifi_status, sizeof(wifi_linked_info_stru), 0, sizeof(wifi_linked_info_stru));
            if (wifi_sta_get_ap_info(&wifi_status) != ERRCODE_SUCC)
            {
                continue;
            }
            if (wifi_status.conn_state == WIFI_CONNECTED)
            {
                break;
            }
        }

        if (wifi_status.conn_state == WIFI_CONNECTED)
        {
            break; /* 连接成功退出循环 */
        }
    }

    /* DHCP获取IP地址 */
    // 获取网络接口
    osal_printk("STA DHCP start.\r\n");
    struct netif *netif_p = NULL;
    netif_p = netifapi_netif_find(ifname);
    if (netif_p == NULL)
    {
        return ERRCODE_FAIL;
    }

    // 启动DHCP客户端
    if (netifapi_dhcp_start(netif_p) != ERR_OK)
    {
        osal_printk("STA DHCP Fail.\r\n");
        return ERRCODE_FAIL;
    }

    // 等待绑定成功
    for (uint8_t i = 0; i < DHCP_BOUND_STATUS_MAX_GET_TIMES; i++)
    {
        osal_msleep(500); /* 延时500ms */
        if (netifapi_dhcp_is_bound(netif_p) == ERR_OK)
        {
            osal_printk("STA DHCP bound success.\r\n");
            break;
        }
    }

    // 等待DHCP服务端分配IP地址
    for (uint8_t i = 0; i < WIFI_STA_IP_MAX_GET_TIMES; i++)
    {
        osal_msleep(10); /* 延时10ms */
        if (netif_p->ip_addr.u_addr.ip4.addr != 0)
        {
            osal_printk("STA IP %u.%u.%u.%u\r\n", (netif_p->ip_addr.u_addr.ip4.addr & 0x000000ff),
                   (netif_p->ip_addr.u_addr.ip4.addr & 0x0000ff00) >> 8,
                   (netif_p->ip_addr.u_addr.ip4.addr & 0x00ff0000) >> 16,
                   (netif_p->ip_addr.u_addr.ip4.addr & 0xff000000) >> 24);
            netifapi_netif_common(netif_p, dhcp_clients_info_show, NULL);
            
            mWifiAddr.ip_addr = netif_p->ip_addr.u_addr.ip4.addr;
            mWifiAddr.flag_ipAddrst = 1;

            if (netifapi_dhcp_start(netif_p) != 0)
            {
                osal_printk("STA DHCP Fail.\r\n");
                return ERRCODE_FAIL;
            }

            /* 连接成功 */
            osal_printk("STA connect success.\r\n");
            return ERRCODE_SUCC;
        }
    }

    osal_printk("STA connect fail.\r\n");
    return ERRCODE_FAIL;
}
